// ## jm4bbx
#include "RootBox.h"
#include "groupAdapter/GroupAdapterOldVersion.h"
#include "groupAdapter/GroupAdapter_2_2.h"
#include "../common_bbx/timer/Timer.h"
#include "../runtime/BoxParameterPack.h"
// ## c++
#include <sstream>
// ##jmlib
#include <jmlib/cast.h>
namespace jm4bbx {
	std::string RootBox::timeReportPrefix = "---";
	int RootBox::nextBoxInstanceId_ = 0;

	const columnType RootBox::SIGNAL_IN_COLUMN = columnType(0);
	const columnType RootBox::SIGNAL_OUT_COLUMN = columnType(0);
	const char* RootBox::SIGNAL_IN_PARAMETER_NAME = "signal_in";
	const char* RootBox::SIGNAL_OUT_PARAMETER_NAME = "signal_out";
	const char* RootBox::TIME_REPORT_FACTOR_PARAM_NAME = "time_report";

	RootBox::RootBox(const BoxParamPack& paramsPack, 
		int forceSignalInCount, int forceSignalOutCount)
#ifdef BOBOX_VERSION_OLD
		: basicBox(paramsPack.id, paramsPack.tid, paramsPack.req)
#else
		: basicBox(paramsPack.pack)
#endif
		, boxInstanceId_(nextBoxInstanceId_++) 
		, signalInCount_(0)
		, signalOutCount_(0) {
			if (forceSignalInCount < 0) {
				signalInCount_ = getBoxParameterValue(paramsPack, 
					SIGNAL_IN_PARAMETER_NAME, 0);
			}
			else {
				signalInCount_ = forceSignalInCount;
			}


			if (forceSignalOutCount < 0) {
				signalOutCount_ = getBoxParameterValue(paramsPack, 
					SIGNAL_OUT_PARAMETER_NAME, 0);
			}
			else {
				signalOutCount_ = forceSignalOutCount;
			}
			timeReport_ = getBoxBoolParameterValue(paramsPack, 
				TIME_REPORT_FACTOR_PARAM_NAME, false);
	}

	RootBox::GroupPtr RootBox::getGroupPtr(groupType idx) {
#ifdef BOBOX_VERSION_2_2
		return GroupPtr(new GroupAdapter_2_2(&basicBox::get_group(idx), this, idx));
#endif
#ifdef BOBOX_VERSION_OLD
		return GroupPtr(new GroupAdapterOldVersion(&basicBox::get_group(idx), this, idx));
#endif
	}

	void RootBox::sendSignalOut(int index, bool poisoned /* = false */ ) {
		if (index < signalOutCount_) {
			if (poisoned) {
				send_poisoned(getSignalOutArc(index));
			}
			else {
				send(getSignalOutArc(index), getSignalOutGroup(index));
			}
		}
	}

	void RootBox::sendSignalsOut(bool poisoned /*= false*/) {
		for (int i = 0; i < signalOutCount_; ++i) {
			sendSignalOut(i, poisoned);
		}

		//if (poisoned) {
		//	for (int i = 0; i < signalOutCount_; ++i) {
		//		send_poisoned(getSignalOutArc(i));
		//	}
		//}
		//else {
		//	for (int i = 0; i < signalOutCount_; ++i) {
		//		send(getSignalOutArc(i), getSignalOutGroup(i));
		//	}
		//}
	}

	void RootBox::wait4SignalsIn() {
		for (int i = 0; i < signalInCount_; ++i) {
			receive(getSignalInArc(i), getSignalInGroup(i));
		}
	}

	bool RootBox::getBoxBoolParameterValue(const BoxParamPack& p, 
		const std::string& paramName, 
		const bool defaultValue /*= false*/) {
			return getBoxParameterValue(p, paramName, defaultValue ? 1 : 0) != 0;
	}

	void RootBox::init_impl() {
		LOG_T_EX(BoxRunTracer, getBoxClassType() + " -> init_impl");
		initSignalInGroup();
		initSignalOutGroup();
		initImpl();
	}

	void RootBox::initSignalInGroup() {
		for (int i = 0; i < signalInCount_; ++i) {
			add_group(getSignalInGroup(i), SIGNAL_IN_COLUMN, 
				get_input_descriptors(getSignalInArc(i)));
		}
	}

	void RootBox::initSignalOutGroup() {
		for (int i = 0; i < signalOutCount_; ++i) {
			add_group(getSignalOutGroup(i), SIGNAL_OUT_COLUMN, 
				get_output_descriptors(getSignalOutArc(i)));
		}
	}

	bool RootBox::areSignalsInPoisoned() {
		for (int i = 0; i < signalInCount_; ++i) {
			if (getGroupPtr(getSignalInGroup(i))->isPoisoned()) {
				return true;
			}
		}
		return false;
	}

	void RootBox::sync_mach_etwas() {
		LOG_T_EX(BoxRunTracer, getBoxClassType() + " -> sync_mach_etwas ");
		if (timeReport_) reportMainMeanTime();
		syncMachEtwas();

	}

	std::string RootBox::getBoxClassType() {
		std::string n(typeid(*this).name());
		size_t p = n.find_first_of("<");
		if (p != std::string::npos) {
			n = n.substr(0, p);
		}
		p = n.find_last_of(":");
		if (p != std::string::npos) {
			n = n.substr(p+1);
		}
		return n;
	}


	
	void RootBox::reportMainMeanTime() {
		//std::stringstream ss;
		//ss << timeReportPrefix << ";" 
		//	<< GlobalTimers::getTimer("main")->meanTime().total_milliseconds();
		//LOG_E_EX(TimeTracer, ss.str())

		//timeReportPrefix = getBoxClassType();
		std::cout << timeReportPrefix << ";" 
			<< GlobalTimers::getTimer("main")->meanTime().total_milliseconds()
			<< std::endl;
	}



	std::ostream& operator<<(std::ostream &out, const jm4bbx::BinaryOperationResultIndex& x) {
		out << x.firstTableRowIndex_ << " : " << x.secondTableRowIndex_ ;
		return out;
	}

} // namespace jm4bbx
